bitkeeper revision 1.1662.1.21 (42a80585xAt7ZrRcqmCRm3HvTrL7Mg)
authorcl349@firebug.cl.cam.ac.uk <cl349@firebug.cl.cam.ac.uk>
Thu, 9 Jun 2005 09:01:57 +0000 (09:01 +0000)
committercl349@firebug.cl.cam.ac.uk <cl349@firebug.cl.cam.ac.uk>
Thu, 9 Jun 2005 09:01:57 +0000 (09:01 +0000)
Many files:
  Switch to xenstore for storing persistent information.
Signed-off-by: Mike Wray <mike.wray@hp.com>
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
12 files changed:
tools/python/xen/xend/XendDomain.py
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/XendRoot.py
tools/python/xen/xend/XendVnet.py
tools/python/xen/xend/image.py
tools/python/xen/xend/server/blkif.py
tools/python/xen/xend/server/channel.py
tools/python/xen/xend/server/console.py
tools/python/xen/xend/server/controller.py
tools/python/xen/xend/server/netif.py
tools/python/xen/xend/server/usbif.py
tools/python/xen/xend/xenstore/xsobj.py

index 5819a9e4bf6e9dee43a28f0e866c3a322054627f..11f434a46d2b4c897a920401eead3087748298a3 100644 (file)
@@ -23,7 +23,8 @@ from xen.xend.XendLogging import log
 from xen.xend import scheduler
 from xen.xend.server import channel
 from xen.xend.server import relocate
-from xen.xend import XendDB
+from xen.xend.uuid import getUuid
+from xen.xend.xenstore import XenNode, DBMap
 
 __all__ = [ "XendDomain" ]
 
@@ -40,9 +41,6 @@ class XendDomain:
     """Index of all domains. Singleton.
     """
 
-    """Path to domain database."""
-    dbpath = "domain"
-
     """Dict of domain info indexed by domain id."""
     domains = None
     
@@ -53,7 +51,7 @@ class XendDomain:
         # So we stuff the XendDomain instance (self) into xroot's components.
         xroot.add_component("xen.xend.XendDomain", self)
         self.domains = XendDomainDict()
-        self.db = XendDB.XendDB(self.dbpath)
+        self.dbmap = DBMap(db=XenNode("/domain"))
         eserver.subscribe('xend.virq', self.onVirq)
         self.initial_refresh()
 
@@ -96,11 +94,18 @@ class XendDomain:
         """Refresh initial domain info from db.
         """
         doms = self.xen_domains()
-        for config in self.db.fetchall("").values():
-            domid = int(sxp.child_value(config, 'id'))
-            if domid in doms:
+        self.dbmap.readDB()
+        for domdb in self.dbmap.values():
+            try:
+                domid = int(domdb.id)
+            except:
+                domid = None
+            # XXX if domid in self.domains, then something went wrong
+            if (domid is None) or (domid in self.domains):
+                domdb.delete()
+            elif domid in doms:
                 try:
-                    self._new_domain(config, doms[domid])
+                    self._new_domain(domdb, doms[domid]) 
                 except Exception, ex:
                     log.exception("Error recreating domain info: id=%d", domid)
                     self._delete_domain(domid)
@@ -108,27 +113,20 @@ class XendDomain:
                 self._delete_domain(domid)
         self.refresh(cleanup=True)
 
-    def sync_domain(self, info):
-        """Sync info for a domain to disk.
-
-        info   domain info
-        """
-        self.db.save(str(info.id), info.sxpr())
-
     def close(self):
         pass
 
-    def _new_domain(self, savedinfo, info):
+    def _new_domain(self, db, info):
         """Create a domain entry from saved info.
 
-        @param savedinfo: saved info from the db
+        @param db:        saved info from the db
         @param info:      domain info from xen
         @return: domain
         """
-        uuid = sxp.child_value(savedinfo, 'uuid')
-        dominfo = XendDomainInfo.recreate(savedinfo, info, uuid)
+        log.error(db)
+        log.error(db.uuid)
+        dominfo = XendDomainInfo.recreate(db, info)
         self.domains[dominfo.id] = dominfo
-        self.sync_domain(dominfo)
         return dominfo
 
     def _add_domain(self, info, notify=True):
@@ -141,11 +139,11 @@ class XendDomain:
         for i, d in self.domains.items():
             if i != d.id:
                 del self.domains[i]
-                self.db.delete(str(i))
+                self.dbmap.delete(d.uuid)
         if info.id in self.domains:
             notify = False
         self.domains[info.id] = info
-        self.sync_domain(info)
+        info.exportToDB(save=True)
         if notify:
             eserver.inject('xend.domain.create', [info.name, info.id])
 
@@ -155,12 +153,26 @@ class XendDomain:
         @param id:     domain id
         @param notify: send a domain died event if true
         """
+        try:
+            if self.xen_domain(id):
+                return
+        except:
+            pass
         info = self.domains.get(id)
         if info:
             del self.domains[id]
-            self.db.delete(str(id))
+            info.cleanup()
+            info.delete()
             if notify:
                 eserver.inject('xend.domain.died', [info.name, info.id])
+        # XXX this should not be needed
+        for domdb in self.dbmap.values():
+            try:
+                domid = int(domdb.id)
+            except:
+                domid = None
+            if (domid is None) or (domid == id):
+                domdb.delete()
 
     def reap(self):
         """Look for domains that have crashed or stopped.
@@ -263,8 +275,7 @@ class XendDomain:
         @param config: configuration
         @return: domain
         """
-        dominfo = XendDomainInfo.create(config)
-        self._add_domain(dominfo)
+        dominfo = XendDomainInfo.create(self.dbmap, config)
         return dominfo
 
     def domain_restart(self, dominfo):
@@ -277,7 +288,6 @@ class XendDomain:
                        [dominfo.name, dominfo.id, "begin"])
         try:
             dominfo.restart()
-            self._add_domain(dominfo)
             log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id)
             eserver.inject("xend.domain.restart",
                            [dominfo.name, dominfo.id, "success"])
@@ -297,8 +307,7 @@ class XendDomain:
         """
         config = sxp.child_value(vmconfig, 'config')
         uuid = sxp.child_value(vmconfig, 'uuid')
-        dominfo = XendDomainInfo.restore(config, uuid=uuid)
-        self._add_domain(dominfo)
+        dominfo = XendDomainInfo.restore(self.dbmap, config, uuid=uuid)
         return dominfo
 
     def domain_restore(self, src, progress=False):
@@ -330,8 +339,12 @@ class XendDomain:
             try:
                 info = self.xen_domain(id)
                 if info:
-                    log.info("Creating entry for unknown domain: id=%d", id)
-                    dominfo = XendDomainInfo.recreate(None, info)
+                    uuid = getUuid()
+                    log.info(
+                        "Creating entry for unknown domain: id=%d uuid=%s",
+                        id, uuid)
+                    db = self.dbmap.addChild(uuid)
+                    dominfo = XendDomainInfo.recreate(db, info)
                     self._add_domain(dominfo)
             except Exception, ex:
                 log.exception("Error creating domain info: id=%d", id)
@@ -593,7 +606,7 @@ class XendDomain:
         """
         dominfo = self.domain_lookup(id)
         val = dominfo.device_create(devconfig)
-        self.sync_domain(dominfo)
+        dominfo.exportToDB()
         return val
 
     def domain_device_configure(self, id, devconfig, devid):
@@ -606,7 +619,7 @@ class XendDomain:
         """
         dominfo = self.domain_lookup(id)
         val = dominfo.device_configure(devconfig, devid)
-        self.sync_domain(dominfo)
+        dominfo.exportToDB()
         return val
     
     def domain_device_refresh(self, id, type, devid):
@@ -618,7 +631,7 @@ class XendDomain:
         """
         dominfo = self.domain_lookup(id)
         val = dominfo.device_refresh(type, devid)
-        self.sync_domain(dominfo)
+        dominfo.exportToDB()
         return val
 
     def domain_device_destroy(self, id, type, devid):
@@ -630,7 +643,7 @@ class XendDomain:
         """
         dominfo = self.domain_lookup(id)
         val = dominfo.device_destroy(type, devid)
-        self.sync_domain(dominfo)
+        dominfo.exportToDB()
         return val
 
     def domain_devtype_ls(self, id, type):
index 97d6dbbf31dfbee0ae4826b66e327c56448058f2..16415d78a7c77f470723d5a800cc17d9dc0f2abc 100644 (file)
@@ -30,6 +30,7 @@ from XendError import XendError, VmError
 from xen.xend.XendRoot import get_component
 
 from xen.xend.uuid import getUuid
+from xen.xend.xenstore import DBVar
 
 """Flag for a block device backend domain."""
 SIF_BLK_BE_DOMAIN = (1<<4)
@@ -145,94 +146,92 @@ class XendDomainInfo:
     """
     MINIMUM_RESTART_TIME = 20
 
-    def _create(cls, uuid=None):
-        """Create a vm object with a uuid.
-
-        @param uuid uuid to use
-        @return vm
-        """
-        if uuid is None:
-            uuid = getUuid()
-        vm = cls()
-        vm.uuid = uuid
-        return vm
-
-    _create = classmethod(_create)
-
-    def create(cls, config):
+    def create(cls, parentdb, config):
         """Create a VM from a configuration.
-        If a vm has been partially created and there is an error it
-        is destroyed.
 
+        @param parentdb:  parent db
         @param config    configuration
         @raise: VmError for invalid configuration
         """
-        vm = cls._create()
+        uuid = getUuid()
+        db = parentdb.addChild(uuid)
+        vm = cls(db)
         vm.construct(config)
+        vm.saveDB(sync=True)
         return vm
 
     create = classmethod(create)
 
-    def recreate(cls, savedinfo, info, uuid=None):
+    def recreate(cls, db, info):
         """Create the VM object for an existing domain.
 
-        @param savedinfo: saved info from the domain DB
+        @param db:        domain db
         @param info:      domain info from xc
-        @param uuid:      uuid to use
-        @type  info:      xc domain dict
         """
-        vm = cls._create(uuid=uuid)
-
-        log.debug('savedinfo=' + prettyprintstring(savedinfo))
+        dom = info['dom']
+        vm = cls(db)
+        db.readDB()
+        vm.importFromDB()
+        config = vm.config
         log.debug('info=' + str(info))
+        log.debug('config=' + prettyprintstring(config))
 
-        vm.recreate = True
-        vm.savedinfo = savedinfo
-        vm.setdom(info['dom'])
+        vm.setdom(dom)
         vm.memory = info['mem_kb']/1024
 
-        start_time = sxp.child_value(savedinfo, 'start_time')
-        if start_time is not None:
-            vm.start_time = float(start_time)
-        vm.restart_state = sxp.child_value(savedinfo, 'restart_state')
-        vm.restart_count = int(sxp.child_value(savedinfo, 'restart_count', 0))
-        restart_time = sxp.child_value(savedinfo, 'restart_time')
-        if restart_time is not None:
-            vm.restart_time = float(restart_time)
-        config = sxp.child_value(savedinfo, 'config')
-
         if config:
-            vm.construct(config)
+            try:
+                vm.recreate = True
+                vm.construct(config)
+            finally:
+                vm.recreate = False
         else:
-            vm.setName(sxp.child_value(savedinfo, 'name',
-                                       "Domain-%d" % info['dom']))
-        vm.recreate = False
-        vm.savedinfo = None
+            vm.setName("Domain-%d" % dom)
 
+        vm.exportToDB(save=True)
         return vm
 
     recreate = classmethod(recreate)
 
-    def restore(cls, config, uuid=None):
+    def restore(cls, parentdb, config, uuid=None):
         """Create a domain and a VM object to do a restore.
 
+        @param parentdb:  parent db
         @param config:    domain configuration
         @param uuid:      uuid to use
         """
-        vm = cls._create(uuid=uuid)
+        db = parentdb.addChild(uuid)
+        vm = cls(db)
         dom = xc.domain_create()
         vm.setdom(dom)
         vm.dom_construct(vm.id, config)
+        vm.saveDB(sync=True)
         return vm
 
     restore = classmethod(restore)
 
-    def __init__(self):
+    __exports__ = [
+        DBVar('id',            ty='str'),
+        DBVar('name',          ty='str'),
+        DBVar('uuid',          ty='str'),
+        DBVar('config',        ty='sxpr'),
+        DBVar('start_time',    ty='float'),
+        DBVar('state',         ty='str'),
+        DBVar('store_mfn',     ty='long'),
+        DBVar('restart_mode',  ty='str'),
+        DBVar('restart_state', ty='str'),
+        DBVar('restart_time',  ty='float'),
+        DBVar('restart_count', ty='int'),
+        ]
+    
+    def __init__(self, db):
+        self.db = db
+        self.uuid = db.getName()
+
         self.recreate = 0
         self.restore = 0
         
         self.config = None
-        self.uuid = None
         self.id = None
         self.cpu_weight = 1
         self.start_time = None
@@ -262,23 +261,39 @@ class XendDomainInfo:
         self.restart_count = 0
         
         self.console_port = None
-        self.savedinfo = None
         self.vcpus = 1
         self.bootloader = None
 
+    def setDB(self, db):
+        self.db = db
+
+    def saveDB(self, save=False, sync=False):
+        self.db.saveDB(save=save, sync=sync)
+
+    def exportToDB(self, save=False, sync=False):
+        if self.channel:
+            self.channel.saveToDB(self.db.addChild("channel"))
+        if self.store_channel:
+            self.store_channel.saveToDB(self.db.addChild("store_channel"))
+        self.db.exportToDB(self, fields=self.__exports__, save=save, sync=sync)
+
+    def importFromDB(self):
+        self.db.importFromDB(self, fields=self.__exports__)
+
     def setdom(self, dom):
         """Set the domain id.
 
         @param dom: domain id
         """
         self.id = int(dom)
+        #self.db.id = self.id
 
     def getDomain(self):
         return self.id
 
     def setName(self, name):
         self.name = name
-        #self.db.name = self.name
+        self.db.name = self.name
 
     def getName(self):
         return self.name
@@ -301,6 +316,7 @@ class XendDomainInfo:
             self.state = state
             self.state_updated.notifyAll()
         self.state_updated.release()
+        self.saveDB()
 
     def state_wait(self, state):
         self.state_updated.acquire()
@@ -484,6 +500,7 @@ class XendDomainInfo:
             self.configure_restart()
             self.construct_image()
             self.configure()
+            self.exportToDB()
         except Exception, ex:
             # Catch errors, cleanup and re-raise.
             print 'Domain construction error:', ex
@@ -495,6 +512,7 @@ class XendDomainInfo:
     def register_domain(self):
         xd = get_component('xen.xend.XendDomain')
         xd._add_domain(self)
+        self.exportToDB()
 
     def configure_cpus(self, config):
         try:
@@ -528,42 +546,26 @@ class XendDomainInfo:
         """
         self.create_channel()
         self.image.createImage()
-        #self.image.exportToDB()
+        self.image.exportToDB()
         #if self.store_channel:
         #    self.db.introduceDomain(self.id,
         #                            self.store_mfn,
         #                            self.store_channel)
 
-    def get_device_savedinfo(self, type, id):
-        val = None
-        if self.savedinfo is None:
-            return val
-        devices = sxp.child(self.savedinfo, 'devices')
-        if devices is None:
-            return val
-        for d in sxp.children(devices, type):
-            did = sxp.child_value(d, 'id')
-            if did is None: continue
-            if int(did) == id:
-                val = d
-                break
-        return val
-
-    def get_device_recreate(self, type, id):
-        return self.get_device_savedinfo(type, id) or self.recreate
-
-    def destroy(self):
-        """Completely destroy the vm.
+    def delete(self):
+        """Delete the vm's db.
         """
+        if self.dom_get(self.id):
+            return
+        self.id = None
+        self.saveDB(sync=True)
         try:
-            self.cleanup()
-        except Exception, ex:
-            log.warning("error in domain cleanup: %s", ex)
-            pass
-        try:
-            self.destroy_domain()
+            # Todo: eventually will have to wait for devices to signal
+            # destruction before can delete the db.
+            if self.db:
+                self.db.delete()
         except Exception, ex:
-            log.warning("error in domain destroy: %s", ex)
+            log.warning("error in domain db delete: %s", ex)
             pass
 
     def destroy_domain(self):
@@ -571,6 +573,18 @@ class XendDomainInfo:
         The domain will not finally go away unless all vm
         devices have been released.
         """
+        if self.id is None:
+            return
+        try:
+            xc.domain_destroy(dom=self.id)
+        except Exception, err:
+            log.exception("Domain destroy failed: %s", self.name)
+
+    def cleanup(self):
+        """Cleanup vm resources: release devices.
+        """
+        self.state = STATE_VM_TERMINATED
+        self.release_devices()
         if self.channel:
             try:
                 self.channel.close()
@@ -583,23 +597,25 @@ class XendDomainInfo:
                 self.store_channel = None
             except:
                 pass
+            #try:
+            #    self.db.releaseDomain(self.id)
+            #except Exception, ex:
+            #    log.warning("error in domain release on xenstore: %s", ex)
+            #    pass
         if self.image:
             try:
                 self.image.destroy()
                 self.image = None
             except:
                 pass
-        if self.id is None: return 0
-        try:
-            xc.domain_destroy(dom=self.id)
-        except Exception, err:
-            log.exception("Domain destroy failed: %s", self.name)
 
-    def cleanup(self):
-        """Cleanup vm resources: release devices.
+    def destroy(self):
+        """Clenup vm and destroy domain.
         """
-        self.state = STATE_VM_TERMINATED
-        self.release_devices()
+        self.cleanup()
+        self.destroy_domain()
+        self.saveDB()
+        return 0
 
     def is_terminated(self):
         """Check if a domain has been terminated.
@@ -639,27 +655,24 @@ class XendDomainInfo:
         if not self.restore:
             self.setdom(dom)
 
-    def openChannel(self, name, local, remote):
+    def openChannel(self, key, local, remote):
         """Create a channel to the domain.
         If saved info is available recreate the channel.
         
+        @param key db key for the saved data (if any)
         @param local default local port
         @param remote default remote port
         """
-        local = 0
-        remote = 1
-        if self.savedinfo:
-            info = sxp.child(self.savedinfo, name)
-            if info:
-                local = int(sxp.child_value(info, "local_port", 0))
-                remote = int(sxp.child_value(info, "remote_port", 1))
-        chan = channelFactory().openChannel(self.id, local_port=local,
-                                            remote_port=remote)
+        db = self.db.addChild(key)
+        chan = channelFactory().restoreFromDB(db, self.id, local, remote)
+        #todo: save here?
+        #chan.saveToDB(db)
         return chan
 
-    def eventChannel(self, name):
-        return EventChannel.interdomain(0, self.id)
-
+    def eventChannel(self, key):
+        db = self.db.addChild(key)
+        return EventChannel.restoreFromDB(db, 0, self.id)
+        
     def create_channel(self):
         """Create the channels to the domain.
         """
@@ -823,6 +836,7 @@ class XendDomainInfo:
             if self.bootloader:
                 self.config = self.bootloader_config()
             self.construct(self.config)
+            self.saveDB()
         finally:
             self.restart_state = None
 
index 10e560fbcc0c02ffd5235729b3efc5274c8095c8..045a5a5fa45ad75bd53f16c34350ad6df6a8a4cd 100644 (file)
@@ -25,9 +25,6 @@ import sxp
 class XendRoot:
     """Root of the management classes."""
 
-    """Default path to the root of the database."""
-    dbroot_default = "/var/lib/xen/xend-db"
-
     """Default path to the config file."""
     config_default = "/etc/xen/xend-config.sxp"
 
@@ -82,7 +79,6 @@ class XendRoot:
     components = {}
 
     def __init__(self):
-        self.dbroot = None
         self.config_path = None
         self.config = None
         self.logging = None
@@ -171,7 +167,6 @@ class XendRoot:
     def configure(self):
         self.set_config()
         self.configure_logger()
-        self.dbroot = self.get_config_value("dbroot", self.dbroot_default)
 
     def configure_logger(self):
         logfile = self.get_config_value("logfile", self.logfile_default)
@@ -192,11 +187,6 @@ class XendRoot:
         """
         return self.logging and self.logging.getLogger()
 
-    def get_dbroot(self):
-        """Get the path to the database root.
-        """
-        return self.dbroot
-
     def set_config(self):
         """If the config file exists, read it. If not, ignore it.
 
index d95fd204aa39a4ed96d871bac97989b27ce4c3f3..3614127c4912133a8954646c06ce3f8e5b775946 100644 (file)
@@ -4,11 +4,10 @@
 """
 
 from xen.util import Brctl
-
-import sxp
-import XendDB
-from XendError import XendError
-from XendLogging import log
+from xen.xend import sxp
+from xen.xend.XendError import XendError
+from xen.xend.XendLogging import log
+from xen.xend.xenstore import XenNode, DBMap
 
 def vnet_cmd(cmd):
     out = None
@@ -63,14 +62,15 @@ class XendVnet:
     """Index of all vnets. Singleton.
     """
 
-    dbpath = "vnet"
+    dbpath = "/vnet"
 
     def __init__(self):
         # Table of vnet info indexed by vnet id.
         self.vnet = {}
-        self.db = XendDB.XendDB(self.dbpath)
-        vnets = self.db.fetchall("")
-        for config in vnets.values():
+        self.dbmap = DBMap(db=XenNode(self.dbpath))
+        self.dbmap.readDB()
+        for vnetdb in self.dbmap.values():
+            config = vnetdb.config
             info = XendVnetInfo(config)
             self.vnet[info.id] = info
             try:
@@ -115,7 +115,7 @@ class XendVnet:
         """
         info = XendVnetInfo(config)
         self.vnet[info.id] = info
-        self.db.save(info.id, info.sxpr())
+        self.dbmap["%s/config" % info.id] = info.sxpr()
         info.configure()
 
     def vnet_delete(self, id):
@@ -126,7 +126,7 @@ class XendVnet:
         info = self.vnet_get(id)
         if info:
             del self.vnet[id]
-            self.db.delete(id)
+            self.dbmap.delete(id)
             info.delete()
 
 def instance():
index d7f69657672d5d2f41441f01a8385257ee1ab86e..e0d70581bf5f09b3a93a6088c0bdc085209f5574 100644 (file)
@@ -4,7 +4,7 @@ import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
 from xen.xend import sxp
 from xen.xend.XendError import VmError
 from xen.xend.XendLogging import log
-#from xen.xend.xenstore import DBVar
+from xen.xend.xenstore import DBVar
 
 class ImageHandler:
     """Abstract base class for image handlers.
@@ -73,7 +73,7 @@ class ImageHandler:
     #======================================================================
     # Instance vars and methods.
 
-    #db = None
+    db = None
     ostype = None
 
     config = None
@@ -82,25 +82,25 @@ class ImageHandler:
     cmdline = None
     flags = 0
 
-    #__exports__ = [
-    #    DBVar('ostype',  ty='str'),
-    #    DBVar('config',  ty='sxpr'),
-    #    DBVar('kernel',  ty='str'),
-    #    DBVar('ramdisk', ty='str'),
-    #    DBVar('cmdline', ty='str'),
-    #    DBVar('flags',   ty='int'),
-    #    ]
+    __exports__ = [
+        DBVar('ostype',  ty='str'),
+        DBVar('config',  ty='sxpr'),
+        DBVar('kernel',  ty='str'),
+        DBVar('ramdisk', ty='str'),
+        DBVar('cmdline', ty='str'),
+        DBVar('flags',   ty='int'),
+        ]
 
     def __init__(self, vm, config):
         self.vm = vm
-        #self.db = vm.db.addChild('/image')
+        self.db = vm.db.addChild('/image')
         self.config = config
 
-    #def exportToDB(self, save=False):
-    #    self.db.exportToDB(self, fields=self.__exports__, save=save)
+    def exportToDB(self, save=False):
+        self.db.exportToDB(self, fields=self.__exports__, save=save)
 
-    #def importFromDB(self):
-    #    self.db.importFromDB(self, fields=self.__exports__)
+    def importFromDB(self):
+        self.db.importFromDB(self, fields=self.__exports__)
 
     def unlink(self, f):
         if not f: return
@@ -234,11 +234,11 @@ class Plan9ImageHandler(ImageHandler):
 
 class VmxImageHandler(ImageHandler):
 
-    #__exports__ = ImageHandler.__exports__ + [
-    #    DBVar('memmap',        ty='str'),
-    #    DBVar('memmap_value',  ty='sxpr'),
-    #    # device channel?
-    #    ]
+    __exports__ = ImageHandler.__exports__ + [
+        DBVar('memmap',        ty='str'),
+        DBVar('memmap_value',  ty='sxpr'),
+        # device channel?
+        ]
     
     ostype = "vmx"
     memmap = None
index 602979fed0890b10ae02af61a8d3870713530337..75a76e8bda1e41f60f4d7d96a3d9b48ce77754e6 100755 (executable)
@@ -9,6 +9,7 @@ from xen.xend.XendRoot import get_component
 from xen.xend.XendLogging import log
 from xen.xend import sxp
 from xen.xend import Blkctl
+from xen.xend.xenstore import DBVar
 
 from xen.xend.server import channel
 from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
@@ -158,6 +159,18 @@ class BlkDev(Dev):
     """Info record for a block device.
     """
 
+    __exports__ = Dev.__exports__ + [
+        DBVar('dev',          ty='str'),
+        DBVar('vdev',         ty='int'),
+        DBVar('mode',         ty='str'),
+        DBVar('viftype',      ty='str'),
+        DBVar('params',       ty='str'),
+        DBVar('node',         ty='str'),
+        DBVar('device',       ty='long'),
+        DBVar('start_sector', ty='long'),
+        DBVar('nr_sectors',   ty='long'),
+        ]
+
     def __init__(self, controller, id, config, recreate=False):
         Dev.__init__(self, controller, id, config, recreate=recreate)
         self.dev = None
@@ -215,8 +228,7 @@ class BlkDev(Dev):
 
     def attach(self, recreate=False, change=False):
         if recreate:
-            node = sxp.child_value(recreate, 'node')
-            self.setNode(node)
+            pass
         else:
             node = Blkctl.block('bind', self.type, self.params)
             self.setNode(node)
@@ -299,7 +311,8 @@ class BlkDev(Dev):
         return self.controller.getBackend(self.backendDomain)
 
     def refresh(self):
-        log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain, self.id)
+        log.debug("Refreshing vbd domain=%d id=%s", self.frontendDomain,
+                  self.id)
         self.interfaceChanged()
 
     def destroy(self, change=False, reboot=False):
@@ -308,7 +321,8 @@ class BlkDev(Dev):
         @param change: change flag
         """
         self.destroyed = True
-        log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain, self.id)
+        log.debug("Destroying vbd domain=%d id=%s", self.frontendDomain,
+                  self.id)
         self.send_be_vbd_destroy()
         if change:
             self.interfaceChanged()
@@ -445,5 +459,4 @@ class BlkifController(DevController):
                 log.error("Exception connecting backend: %s", ex)
         else:
             log.error('interface connect on unknown interface: id=%d', id)
-    
 
index e5b82b5330a13cd58479e55e8301ba633684c1aa..00f451a7b833e14348a8b7d6c164649697987985 100755 (executable)
@@ -31,6 +31,33 @@ class EventChannel(dict):
 
     interdomain = classmethod(interdomain)
 
+    def restoreFromDB(cls, db, dom1, dom2, port1=0, port2=0):
+        """Create an event channel using db info if available.
+        Inverse to saveToDB().
+
+        @param db db
+        @param dom1
+        @param dom2
+        @param port1
+        @param port2
+        """
+        try:
+            dom1  = int(db['dom1'])
+        except: pass
+        try:
+            dom2  = int(db['dom2'])
+        except: pass
+        try:
+            port1 = int(db['port1'])
+        except: pass
+        try:
+            port2 = int(db['port2'])
+        except: pass
+        evtchn = cls.interdomain(dom1, dom2, port1=port1, port2=port2)
+        return evtchn
+
+    restoreFromDB = classmethod(restoreFromDB)
+
     def __init__(self, dom1, dom2, d):
         d['dom1'] = dom1
         d['dom2'] = dom2
@@ -54,6 +81,18 @@ class EventChannel(dict):
         evtchn_close(self.dom1, self.port1)
         evtchn_close(self.dom2, self.port2)
 
+    def saveToDB(self, db):
+        """Save the event channel to the db so it can be restored later,
+        using restoreFromDB() on the class.
+
+        @param db db
+        """
+        db['dom1']  = str(self.dom1)
+        db['dom2']  = str(self.dom2)
+        db['port1'] = str(self.port1)
+        db['port2'] = str(self.port2)
+        db.saveDB()
+
     def sxpr(self):
         return ['event-channel',
                 ['dom1',  self.dom1  ],
@@ -63,7 +102,7 @@ class EventChannel(dict):
                 ]
 
     def __repr__(self):
-        return ("<EventChannel dom1:%s:%s dom2:%s:%s>"
+        return ("<EventChannel dom1:%d:%d dom2:%d:%d>"
                 % (self.dom1, self.port1, self.dom2, self.port2))
 
 def eventChannel(dom1, dom2, port1=0, port2=0):
@@ -241,8 +280,31 @@ class ChannelFactory:
         @type  remote: int
         @return: port object
         """
-        return xu.port(dom, local_port=int(local_port),
-                       remote_port=int(remote_port))
+        return xu.port(dom, local_port=local_port, remote_port=remote_port)
+
+    def restoreFromDB(self, db, dom, local, remote):
+        """Create a channel using ports restored from the db (if available).
+        Otherwise use the given ports. This is the inverse operation to
+        saveToDB() on a channel.
+
+        @param db db
+        @param dom  domain the channel connects to
+        @param local default local port
+        @param remote default remote port
+        """
+        try:
+            local_port  = int(db['local_port'])
+        except:
+            local_port = local
+        try:
+            remote_port = int(db['remote_port'])
+        except:
+            remote_port = remote
+        try:
+            chan = self.openChannel(dom, local_port, remote_port)
+        except:
+            return None
+        return chan
 
 def channelFactory():
     """Singleton constructor for the channel factory.
@@ -277,6 +339,17 @@ class Channel:
         # Make sure the port will deliver all the messages.
         self.port.register(TYPE_WILDCARD)
 
+    def saveToDB(self, db):
+        """Save the channel ports to the db so the channel can be restored later,
+        using restoreFromDB() on the factory.
+
+        @param db db
+        """
+        if self.closed: return
+        db['local_port'] = str(self.getLocalPort())
+        db['remote_port'] = str(self.getRemotePort())
+        db.saveDB()
+
     def getKey(self):
         """Get the channel key.
         """
index 1d0489de7ff7fa37af5f9858842e0548103e7327..743ace4aecd1787677944433456e92fbcfae09ee 100755 (executable)
@@ -13,6 +13,7 @@ from xen.xend import EventServer; eserver = EventServer.instance()
 from xen.xend.XendLogging import log
 from xen.xend import XendRoot; xroot = XendRoot.instance()
 from xen.xend import sxp
+from xen.xend.xenstore import DBVar
 
 from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
 from xen.xend.server.messages import *
@@ -76,6 +77,12 @@ class ConsoleDev(Dev, protocol.ServerFactory):
     STATUS_CONNECTED = 'connected'
     STATUS_LISTENING = 'listening'
 
+    __exports__ = Dev.__exports__ + [
+        DBVar('status',       ty='str'),
+        #DBVar('listening',    ty='str'),
+        DBVar('console_port', ty='int'),
+        ]
+
     def __init__(self, controller, id, config, recreate=False):
         Dev.__init__(self, controller, id, config)
         self.lock = threading.RLock()
index 5f99545fb903f1140f8e68657b8d790668837adf..d1e19efee1ef59beb7fce3233b5cd944477c9d52 100755 (executable)
@@ -4,6 +4,7 @@ for a domain.
 """
 
 from xen.xend.XendError import XendError
+from xen.xend.xenstore import DBVar
 from xen.xend.server.messages import msgTypeName, printMsg, getMessageType
 
 DEBUG = 0
@@ -155,11 +156,16 @@ class DevController:
 
     """
 
+    # State:
+    # controller/<type> : for controller
+    # device/<type>/<id>   : for each device
+
     def createDevController(cls, vm, recreate=False):
         """Class method to create a dev controller.
         """
         ctrl = cls(vm, recreate=recreate)
         ctrl.initController(recreate=recreate)
+        ctrl.exportToDB()
         return ctrl
 
     createDevController = classmethod(createDevController)
@@ -169,16 +175,38 @@ class DevController:
 
     getType = classmethod(getType)
 
+    __exports__ = [
+        DBVar('type',      'str'),
+        DBVar('destroyed', 'bool'),
+        ]
+
     # Set when registered.
     type = None
 
     def __init__(self, vm, recreate=False):
         self.destroyed = False
         self.vm = vm
+        self.db = self.getDB()
         self.deviceId = 0
         self.devices = {}
         self.device_order = []
 
+    def getDB(self):
+        """Get the db node to use for a controller.
+        """
+        return self.vm.db.addChild("/controller/%s" % self.getType())
+
+    def getDevDB(self, id):
+        """Get the db node to use for a device.
+        """
+        return self.vm.db.addChild("/device/%s/%s" % (self.getType(), id))
+
+    def exportToDB(self, save=False):
+        self.db.exportToDB(self, fields=self.__exports__, save=save)
+
+    def importFromDB(self):
+        self.db.importFromDB(self, fields=self.__exports__)
+
     def getDevControllerType(self):
         return self.dctype
 
@@ -229,15 +257,15 @@ class DevController:
         If change is true the device is a change to an existing domain,
         i.e. it is being added at runtime rather than when the domain is created.
         """
-        # skanky hack: we use the device ids to maybe find the savedinfo
-        # of the device...
-        id = self.nextDeviceId()
-        if recreate:
-            recreate = self.vm.get_device_savedinfo(self.getType(), id)
-        dev = self.newDevice(id, config, recreate=recreate)
+        dev = self.newDevice(self.nextDeviceId(), config, recreate=recreate)
+        if self.vm.recreate:
+            dev.importFromDB()
         dev.init(recreate=recreate)
         self.addDevice(dev)
+        if not recreate:
+            dev.exportToDB()
         dev.attach(recreate=recreate, change=change)
+        dev.exportToDB()
 
     def configureDevice(self, id, config, change=False):
         """Reconfigure an existing device.
@@ -344,11 +372,42 @@ class Dev:
     @type controller: DevController
     """
     
+    # ./status       : need 2: actual and requested?
+    # down-down: initial.
+    # up-up: fully up.
+    # down-up: down requested, still up. Watch front and back, when both
+    # down go to down-down. But what if one (or both) is not connected?
+    # Still have front/back trees with status? Watch front/status, back/status?
+    # up-down: up requested, still down.
+    # Back-end watches ./status, front/status
+    # Front-end watches ./status, back/status
+    # i.e. each watches the other 2.
+    # Each is status/request status/actual?
+    #
+    # backend?
+    # frontend?
+
+    __exports__ = [
+        DBVar('id',        ty='int'),
+        DBVar('type',      ty='str'),
+        DBVar('config',    ty='sxpr'),
+        DBVar('destroyed', ty='bool'),
+        ]
+
     def __init__(self, controller, id, config, recreate=False):
         self.controller = controller
         self.id = id
         self.config = config
         self.destroyed = False
+        self.type = self.getType()
+
+        self.db = controller.getDevDB(id)
+
+    def exportToDB(self, save=False):
+        self.db.exportToDB(self, fields=self.__exports__, save=save)
+
+    def importFromDB(self):
+        self.db.importFromDB(self, fields=self.__exports__)
 
     def getDomain(self):
         return self.controller.getDomain()
index 157490f5a2d175e212b6a88516a9f91dec5058d8..0a498425220249b2efb2ebabaaed503e2d1dee2c 100755 (executable)
@@ -12,6 +12,7 @@ from xen.xend.XendError import XendError, VmError
 from xen.xend.XendLogging import log
 from xen.xend import XendVnet
 from xen.xend.XendRoot import get_component
+from xen.xend.xenstore import DBVar
 
 from xen.xend.server import channel
 from xen.xend.server.controller import CtrlMsgRcvr, Dev, DevController
@@ -21,6 +22,57 @@ class NetDev(Dev):
     """A network device.
     """
 
+    # State:
+    # inherited + 
+    # ./config
+    # ./mac
+    # ./be_mac
+    # ./bridge
+    # ./script
+    # ./ipaddr ?
+    #
+    # ./credit
+    # ./period
+    #
+    # ./vifctl: up/down?
+    # ./vifname
+    #
+    #
+    # Poss should have no backend state here - except for ref to backend's own tree
+    # for the device? And a status - the one we want.
+    # ./back/dom
+    # ./back/devid - id for back-end (netif_handle) - same as front/devid
+    # ./back/id    - backend id (if more than one b/e per domain)
+    # ./back/status
+    # ./back/tx_shmem_frame  - actually these belong in back-end state
+    # ./back/rx_shmem_frame
+    #
+    # ./front/dom
+    # ./front/devid
+    # ./front/status - need 2: one for requested, one for actual? Or drive from dev status
+    # and this is front status only.
+    # ./front/tx_shmem_frame
+    # ./front/rx_shmem_frame
+    #
+    # ./evtchn/front - here or in front/back?
+    # ./evtchn/back
+    # ./evtchn/status ?
+    # At present created by dev: but should be created unbound by front/back
+    # separately and then bound (by back)?
+
+    __exports__ = Dev.__exports__ + [
+        DBVar('config',  ty='sxpr'),
+        DBVar('mac',     ty='mac'),
+        DBVar('be_mac',  ty='mac'),
+        DBVar('bridge',  ty='str'),
+        DBVar('script',  ty='str'),
+        #DBVar('ipaddr'),
+        DBVar('credit',  ty='int'),
+        DBVar('period',  ty='int'),
+        DBVar('vifname', ty='str'),
+        DBVar('evtchn'),                #todo: export fields (renamed)
+        ]
+
     def __init__(self, controller, id, config, recreate=False):
         Dev.__init__(self, controller, id, config, recreate=recreate)
         self.vif = int(self.id)
index 657a98e67112d95664470231136eb601e70c0cec..d366985740164a2677c0e74e3f5da39e249a58f0 100644 (file)
@@ -7,6 +7,7 @@
 from xen.xend import sxp
 from xen.xend.XendLogging import log
 from xen.xend.XendError import XendError
+from xen.xend.xenstore import DBVar
 
 from xen.xend.server import channel
 from xen.xend.server.controller import Dev, DevController
@@ -141,6 +142,11 @@ class UsbBackend:
 
 
 class UsbDev(Dev):
+
+    __exports__ = Dev.__exports__ + [
+        DBVar('port', ty='int'),
+        DBVar('path', ty='str'),
+        ]
     
     def __init__(self, controller, id, config, recreate=False):
         Dev.__init__(self, controller, id, config, recreate=recreate)
index 62e18d07d91c00e46ad8629a70cd11d4b7aeeef8..b1c9a4f1d1ca5769cb02b612d31f9f497dc178d7 100644 (file)
@@ -201,7 +201,10 @@ class DBVar:
         setAttr(o, self.attr, val)
 
     def getDB(self, db):
-        data = getattr(db, self.path)
+        try:
+            data = getattr(db, self.path)
+        except AttributeError:
+            return None
         return DBConverter.convertFromDB(data, ty=self.ty)
 
     def setDB(self, db, val):